package com.boruan.shengtangfeng.core.redis.utils;

import java.io.Serializable;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import lombok.extern.slf4j.Slf4j;

/**
 */
@Slf4j
public class RedisAtomicClient implements Serializable{


	@Autowired
	private StringRedisTemplate stringRedisTemplate;


	/**
	 * 获取redis的分布式锁，内部实现使用了redis的setnx。只会尝试一次，如果锁定失败返回null，如果锁定成功则返回RedisLock对象，调用方需要调用RedisLock.unlock()方法来释放锁.
	 * <br/>
	 * 使用方法：
	 * 
	 * <pre>
	 * RedisLock lock = redisAtomicClient.getLock(key, 2);
	 * if (lock != null) {
	 * 	try {
	 * 		// lock succeed, do something
	 * 	} finally {
	 * 		lock.unlock();
	 * 	}
	 * }
	 * </pre>
	 * 
	 * 由于RedisLock实现了AutoCloseable,所以可以使用更简介的使用方法:
	 * 
	 * <pre>
	 * try (RedisLock lock = redisAtomicClient.getLock(key, 2)) {
	 * 	if (lock != null) {
	 * 		// lock succeed, do something
	 * 	}
	 * }
	 * </pre>
	 * 
	 * @param key
	 *            要锁定的key
	 * @param timeout
	 *            	超时时间毫秒
	 * @return 获得的锁对象（如果为null表示获取锁失败），后续可以调用该对象的unlock方法来释放锁.
	 */
	public RedisLock getLock(String key, long timeout){
		String value = String.valueOf(timeout + System.currentTimeMillis());   
		if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){
			log.debug("加锁成功：{}",key);
			return new RedisLockInner(stringRedisTemplate, key, value);  
		}
		//判断锁超时,防止死锁
		String currentValue = (String)stringRedisTemplate.opsForValue().get(key);
		//如果锁过期
		if(StringUtils.isNotEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){
			//获取上一个锁的时间value
			String oldValue = (String) stringRedisTemplate.opsForValue().getAndSet(key,value);
			//校验是不是上个对应的商品时间戳,也是防止并发
			if(StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue) ){
				log.info("加锁成功：{}",key);
				return new RedisLockInner(stringRedisTemplate, key, value);    
			}
		}
		return null;    
	}

    
    private class RedisLockInner implements RedisLock{
        private StringRedisTemplate stringRedisTemplate;
        private String key;
        private String expectedValue;
 
        protected RedisLockInner(StringRedisTemplate stringRedisTemplate, String key, String expectedValue){
            this.stringRedisTemplate = stringRedisTemplate;
            this.key = key;
            this.expectedValue = expectedValue;
        }
 
        /**
         * 释放redis分布式锁
         */
        @Override
        public void unlock(){
        	 try {
        		 String currentValue =  (String) stringRedisTemplate.opsForValue().get(key);
        		 if(!StringUtils.isEmpty(currentValue) && currentValue.equals(expectedValue) ){
        			 //删除key            
        			 stringRedisTemplate.opsForValue().getOperations().delete(key);
        		 }
        		 log.debug("解锁成功：{}",key);
        	 } catch (Exception e) {
        			 log.error("[Redis分布式锁] 解锁出现异常了，{}",e);        
        	 }
       }

 
        @Override
        public void close() throws Exception {
            this.unlock();
        }
    }
}